﻿// =============================================
// 引入必要的命名空间：
// UnityEditor：提供编辑器相关的 API，比如 MaterialEditor、GUILayout 等
// UnityEngine：基础 Unity API
// UnityEngine.Rendering：提供渲染相关枚举，比如 BlendMode、RenderQueue
// =============================================
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;

// =============================================
// 自定义 ShaderGUI 类，用于扩展 Unity 材质面板的显示与交互逻辑
// 通过继承自 ShaderGUI，可以自定义材质属性在 Inspector 中的布局和行为
// =============================================
public class CustomShaderGUI : ShaderGUI
{
	// =============================================
	// 编辑器相关引用
	// =============================================
	MaterialEditor editor;          // 当前正在编辑的 MaterialEditor 实例
	Object[] materials;             // 当前选中的所有材质对象（可能多个）
	MaterialProperty[] properties;  // 当前 Shader 中定义的所有 MaterialProperty 属性

	// =============================================
	// UI 控制变量
	// =============================================
	bool showPresets;  // 控制是否显示“预设”折叠面板

	// =============================================
	// 封装的属性设置方法与状态读取（便捷方法）
	// =============================================

	// 是否启用 Alpha Clipping（硬透明裁剪），对应 Shader 中的 _Clipping 和 _CLIPPING 关键词
	bool Clipping
	{
		set => SetProperty("_Clipping", "_CLIPPING", value);
	}

	// 是否具有 _PremulAlpha 属性（用于判断是否显示“预乘透明”预设）
	bool HasPremultiplyAlpha => HasProperty("_PremulAlpha");

	// 是否启用 Premultiplied Alpha 混合，对应 _PremulAlpha 和 _PREMULTIPLY_ALPHA
	bool PremultiplyAlpha
	{
		set => SetProperty("_PremulAlpha", "_PREMULTIPLY_ALPHA", value);
	}

	// 源混合模式（SrcBlend），比如 SrcAlpha、One 等
	BlendMode SrcBlend
	{
		set => SetProperty("_SrcBlend", (float)value);
	}

	// 目标混合模式（DstBlend），比如 OneMinusSrcAlpha、Zero 等
	BlendMode DstBlend
	{
		set => SetProperty("_DstBlend", (float)value);
	}

	// 是否启用深度写入（ZWrite），对应 _ZWrite
	bool ZWrite
	{
		set => SetProperty("_ZWrite", value ? 1f : 0f);
	}

	// 渲染队列（RenderQueue），比如 Geometry、Transparent、AlphaTest
	RenderQueue RenderQueue
	{
		set
		{
			foreach (Material m in materials)
			{
				m.renderQueue = (int)value;  // 直接设置材质的渲染队列数值
			}
		}
	}

	// =============================================
	// Unity 要求重写的方法：自定义 Material Inspector 的 GUI 内容
	// 当用户在 Inspector 中选中使用了该 Shader 的材质时，会调用此方法
	// =============================================
	public override void OnGUI(
		MaterialEditor materialEditor, MaterialProperty[] properties
	)
	{
		base.OnGUI(materialEditor, properties); // 先调用基类默认的 GUI（显示默认属性）

		// 保存引用，便于后续使用
		editor = materialEditor;
		materials = materialEditor.targets;     // 可能同时选中多个材质
		this.properties = properties;           // 当前 Shader 的所有属性

		EditorGUILayout.Space();  // 添加一个空行间隔

		// 显示一个可折叠的 "Presets"（预设）面板
		showPresets = EditorGUILayout.Foldout(showPresets, "Presets", true);
		if (showPresets)
		{
			OpaquePreset();    // 不透明预设
			ClipPreset();      // Alpha 裁剪（硬透明）预设
			FadePreset();      // 渐隐（标准透明）预设
			TransparentPreset(); // 预乘透明预设（可选，只有存在 _PremulAlpha 时才显示按钮）
		}
	}

	// =============================================
	// 预设功能：不透明物体（Opaque）
	// 常用于普通不透明物体，比如石头、盒子等，不需要透明也不裁剪
	// =============================================
	void OpaquePreset()
	{
		if (PresetButton("Opaque"))
		{
			Clipping = false;              // 不启用 Alpha 裁剪
			PremultiplyAlpha = false;      // 不启用预乘 Alpha
			SrcBlend = BlendMode.One;      // 混合模式：One（不透明）
			DstBlend = BlendMode.Zero;     // 混合模式：Zero
			ZWrite = true;                 // 开启深度写入
			RenderQueue = RenderQueue.Geometry; // 渲染队列：Geometry（不透明物体默认队列）
		}
	}

	// =============================================
	// 预设功能：Alpha 裁剪模式（Clip）
	// 用于硬透明效果，比如铁丝网、叶片，通过 _Cutoff 裁剪掉透明部分
	// =============================================
	void ClipPreset()
	{
		if (PresetButton("Clip"))
		{
			Clipping = true;               // 启用 Alpha 裁剪 (_CLIPPING)
			PremultiplyAlpha = false;      // 不启用预乘
			SrcBlend = BlendMode.One;      // 混合模式：One
			DstBlend = BlendMode.Zero;     // 混合模式：Zero
			ZWrite = true;                 // 开启深度写入
			RenderQueue = RenderQueue.AlphaTest; // 渲染队列：AlphaTest（裁剪物体）
		}
	}

	// =============================================
	// 预设功能：Fade 模式（标准透明）
	// 用于标准透明物体，比如玻璃、水，支持渐变透明，使用 SrcAlpha 混合
	// =============================================
	void FadePreset()
	{
		if (PresetButton("Fade"))
		{
			Clipping = false;              // 不启用裁剪
			PremultiplyAlpha = false;      // 不启用预乘
			SrcBlend = BlendMode.SrcAlpha; // 源混合：SrcAlpha
			DstBlend = BlendMode.OneMinusSrcAlpha; // 目标混合：1 - SrcAlpha
			ZWrite = false;                // 关闭深度写入（透明物体通常不写入深度）
			RenderQueue = RenderQueue.Transparent; // 渲染队列：Transparent
		}
	}

	// =============================================
	// 预设功能：Transparent 模式（预乘透明）
	// 用于预乘 Alpha 的透明物体，优化渲染，仅当存在 _PremulAlpha 属性时显示
	// =============================================
	void TransparentPreset()
	{
		if (HasPremultiplyAlpha && PresetButton("Transparent"))
		{
			Clipping = false;              // 不启用裁剪
			PremultiplyAlpha = true;       // 启用预乘 Alpha
			SrcBlend = BlendMode.One;      // 混合模式：One（预乘后）
			DstBlend = BlendMode.OneMinusSrcAlpha; // 目标混合：1 - SrcAlpha
			ZWrite = false;                // 关闭深度写入
			RenderQueue = RenderQueue.Transparent; // 渲染队列：Transparent
		}
	}

	// =============================================
	// 工具方法：创建一个预设按钮，并在点击时注册 Undo
	// =============================================
	bool PresetButton(string name)
	{
		if (GUILayout.Button(name))
		{
			editor.RegisterPropertyChangeUndo(name); // 注册一个可撤销的操作
			return true;
		}
		return false;
	}

	// =============================================
	// 工具方法：检查当前 Shader 是否包含某个属性（比如 _PremulAlpha）
	// =============================================
	bool HasProperty(string name)
	{
		return FindProperty(name, properties, false) != null;
	}

	// =============================================
	// 工具方法：设置一个 float 类型的 Material 属性（比如 _Clipping = 1 或 0）
	// 并可选地设置对应的 Shader Keyword（比如 _CLIPPING）
	// =============================================
	bool SetProperty(string name, string keyword, bool value)
	{
		if (SetProperty(name, value ? 1f : 0f))
		{
			SetKeyword(keyword, value);
			return true;
		}
		return false;
	}

	// =============================================
	// 工具方法：设置一个 float 类型的 Material Property（比如 _SrcBlend = 1）
	// =============================================
	bool SetProperty(string name, float value)
	{
		MaterialProperty property = FindProperty(name, properties, false);
		if (property != null)
		{
			property.floatValue = value;
			return true;
		}
		return false;
	}

	// =============================================
	// 工具方法：为当前选中的所有材质启用或禁用某个 Shader Keyword
	// 比如启用 _CLIPPING 或 _PREMULTIPLY_ALPHA
	// =============================================
	void SetKeyword(string keyword, bool enabled)
	{
		if (enabled)
		{
			foreach (Material m in materials)
			{
				m.EnableKeyword(keyword);
			}
		}
		else
		{
			foreach (Material m in materials)
			{
				m.DisableKeyword(keyword);
			}
		}
	}
}